﻿using Microsoft.Data.SqlClient;
using System;
using System.Collections.Concurrent;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;

namespace DbAccess
{
    public abstract class IDataBase
    {
        private string _connString = null!;

        public string ConnString
        {
            get { return _connString; }
            set
            {
                if (DbType == DbBaseType.Sqlite)
                {
                    if (!File.Exists(value))
                        throw new FileNotFoundException("数据库文件未找到，请检查！");
                    _connString = string.Format("Data Source={0};", value);
                }
                else
                    _connString = value;
            }
        }

        public DbBaseType DbType { get; set; }
        public string Password { get; set; }
        public IDbTransaction Transaction { get; set; }

        public virtual DbConnection DbConnection(DbBaseType dbType)
        {
            if (string.IsNullOrWhiteSpace(ConnString))
                throw new Exception("请检查数据库连接地址");
            DbConnection conn = default;
            switch (dbType)
            {
                case DbBaseType.Sqlite:
                    SQLiteConnectionStringBuilder connstr = new SQLiteConnectionStringBuilder
                    {
                        DataSource = ConnString,
                        Password = Password
                    };
                    var sqliteConn = new SQLiteConnection(ConnString);

                    conn = sqliteConn;
                    break;

                case DbBaseType.MySql:
                    conn = new MySqlConnector.MySqlConnection(ConnString);
                    break;
                case DbBaseType.SqlServer:
                    conn = new SqlConnection(ConnString);
                    break;
            }
            return conn;
        }

        #region 低版本不支持使用DbProviderFactories.GetFactory(connection)，高版本支持，所以此处使用Emit获取DbConnection对象的DbProviderFactory属性

        private readonly ConcurrentDictionary<Type, Func<DbConnection, DbProviderFactory>> Cache = new ConcurrentDictionary<Type, Func<DbConnection, DbProviderFactory>>();

        public DbProviderFactory GetFactory(DbConnection connection)
        => Cache.GetOrAdd(connection.GetType(), CreateFactoryFunc)(connection);// 如果版本高使用此： DbProviderFactories.GetFactory(connection)

        private Func<DbConnection, DbProviderFactory> CreateFactoryFunc(Type connectionType)
        {
            DynamicMethod method = new DynamicMethod(
                "GetDbProviderFactory",
                typeof(DbProviderFactory),
                new Type[] { typeof(DbConnection) },
                typeof(IDataBase).Module,
                true);
            ILGenerator il = method.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            PropertyInfo propertyInfo = connectionType.GetProperty("DbProviderFactory", BindingFlags.Instance | BindingFlags.NonPublic);
            MethodInfo getMethod = propertyInfo.GetGetMethod(true);
            il.Emit(OpCodes.Callvirt, getMethod);
            il.Emit(OpCodes.Ret);
            return (Func<DbConnection, DbProviderFactory>)method.CreateDelegate(typeof(Func<DbConnection, DbProviderFactory>));
        }

        #endregion 低版本不支持使用DbProviderFactories.GetFactory(connection)，高版本支持，所以此处使用Emit获取DbConnection对象的DbProviderFactory属性

        public abstract DbConnection DbConnection();
    }
}